package com.htfyun.ft.fragment.engineer;

import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;

import com.htfyun.ft.R;
import com.htfyun.ft.fragment.BaseFragment;
import com.htfyun.ft.utils.DebugLog;
import com.htfyun.serialport.ServoSingleton;
import com.htfyun.serialport.uartservo.IServoCommand;
import com.htfyun.serialport.uartservo.IServoControl;
import com.htfyun.serialport.uartservo.IServoReadDataAnalyze;
import com.htfyun.serialport.uartservo.OnServoListener;
import com.htfyun.serialport.uartservo.ServoControlAttr;
import com.htfyun.serialport.uartservo.ServoErrorAttr;
import com.htfyun.serialport.uartservo.ServoInterestedInfo;
import com.xw.repo.BubbleConfigBuilder;
import com.xw.repo.BubbleSeekBar;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class FragmentServo extends BaseFragment implements
        View.OnClickListener,
        BubbleSeekBar.OnProgressChangedListener,
        OnServoListener {

    private TextView txtMovingStatus;
    private TextView txtCurrentPosition;
    private TextView txtCurrentAngularVelocity;

    private Map<SeekBarControlEnum, LayoutSeekBarViewHolder> seekBarControlEnumMap = new HashMap<>();

    private Switch switchEnabled;

    @Override
    protected int getLayoutRes() {
        return R.layout.fragment_engineer_servo;
    }

    @Override
    protected void onInitView(@Nullable Bundle savedInstanceState) {
        initView();
    }

    private void initView() {

        txtMovingStatus = (TextView) findViewById(R.id.txtMovingStatus);
        txtCurrentPosition = (TextView) findViewById(R.id.txtCurrentPosition);
        txtCurrentAngularVelocity = (TextView) findViewById(R.id.txtCurrentAngularVelocity);

        updateCurrentAngularVelocity(0);
        updateCurrentPosition(0);
        updateMovingStatus(false);

        initBtn(R.id.status);
        initBtn(R.id.stop);
        initBtn(R.id.setZero);
        initBtn(R.id.updateServo);

        for (SeekBarControlEnum item : SeekBarControlEnum.values()) {
            View container = findViewById(item.getContainerViewRes());
            if (container == null) {
                continue;
            }
            container.setTag(item);
            seekBarControlEnumMap.put(item, new LayoutSeekBarViewHolder(container));
        }

        initSwitch();

    }

    private void initSwitch() {
        switchEnabled = (Switch) findViewById(R.id.switchEnabled);
        switchEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                setAllViewEnabled(isChecked);

                if (isChecked) {

                    ServoSingleton.INSTANCE.getServoControl().setFeedbackTime_100ms(4);
                    ServoSingleton.INSTANCE.getServoControl().registerServoListener(FragmentServo.this);
                    getStatus();

                } else {

                    ServoSingleton.INSTANCE.getServoControl().unregisterServoListener(FragmentServo.this);
                    ServoSingleton.INSTANCE.getServoControl().servoStop();
                    ServoSingleton.INSTANCE.deInit();

                }
            }
        });

        switchEnabled.setChecked(false);
        setAllViewEnabled(false);

    }

    private void setAllViewEnabled(boolean enabled) {

        View container = findViewById(R.id.container);
        setViewEnabled(container, enabled);

        switchEnabled.setEnabled(true);

    }

    private void setViewEnabled(View view, boolean enabled) {
        if (view == null) return;

        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;

            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                View child = viewGroup.getChildAt(i);
                setViewEnabled(child, enabled);
            }

            return;
        }

        view.setEnabled(enabled);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        ServoSingleton.INSTANCE.getServoControl().unregisterServoListener(FragmentServo.this);
        ServoSingleton.INSTANCE.getServoControl().servoStop();

    }

    private void getStatus() {
        getServoControl().getAngleAreaMin();
        getServoControl().getAngleAreaMax();
        getServoControl().getPosition();
        getServoControl().getAngularVelocity();
        getServoControl().getFeedbackTime_100ms();

    }

    private void initBtn(int resId) {
        View btn = findViewById(resId);
        if (btn != null) {
            btn.setOnClickListener(this);
        }

    }

    @Override
    public void onClick(View v) {
        DebugLog.e("View tag = " + v.getTag());

        final int id = v.getId();
        switch (id) {
            case R.id.sub:
                actionImgSubOrAdd(v, false);
                break;
            case R.id.add:
                actionImgSubOrAdd(v, true);
                break;
            case R.id.status:
                getStatus();
                break;
            case R.id.stop:
                getServoControl().servoStop();
                break;
            case R.id.setZero:
                DebugLog.e("setZero click");
                ServoInterestedInfo info = getServoControl().getServoInterestedInfo();
                getServoControl().servoStop();
                getServoControl().setZeroForServo();

                getHandler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        getServoControl().getPosition();
                    }
                }, 300);
                break;
            case R.id.updateServo:
                Intent intent = new Intent(getContext(), AX12UpdateActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                getContext().startActivity(intent);
                break;

        }
    }

    @Override
    public void onProgressChanged(BubbleSeekBar bubbleSeekBar, int progress, float progressFloat, boolean fromUser) {
    }

    @Override
    public void getProgressOnActionUp(BubbleSeekBar bubbleSeekBar, int progress, float progressFloat) {
//        DebugLog.e("getProgressOnActionUp bubbleSeekBar tag = " + bubbleSeekBar.getTag());
        actionSeekBar(bubbleSeekBar, progress);
    }

    @Override
    public void getProgressOnFinally(BubbleSeekBar bubbleSeekBar, int progress, float progressFloat, boolean fromUser) {
//        DebugLog.e("getProgressOnFinally bubbleSeekBar tag = " + bubbleSeekBar.getTag());

    }

    @Override
    public void onServoListener(List<ServoErrorAttr> errorAttrList, ServoControlAttr controlAttr, ServoControlAttr.Instruction instruction, ServoInterestedInfo info) {

        if (errorAttrList != null && !errorAttrList.isEmpty()) {
            for (ServoErrorAttr errorAttr : errorAttrList) {
                DebugLog.e(" errorAttr = " + errorAttr);
            }
        }

        if (controlAttr == null) {
            DebugLog.e(" controlAttr is null");
            return;
        }

        DebugLog.e("instruction = " + instruction + ", controlAttr = " + controlAttr);

        final ServoInterestedInfo interestedInfo = info;
        final ServoControlAttr cAttr = controlAttr;

        if (getActivity() != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    switch (cAttr) {
                        case angle_area_min:
                            updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum.minAngle, interestedInfo.getAngleAreaMin());
                            break;
                        case angle_area_max:
                            updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum.maxAngle, interestedInfo.getAngleAreaMax());
                            break;
                        case angular_velocity:
                            updateCurrentAngularVelocity(interestedInfo.getAngularVelocity());
                            updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum.angularVelocity, interestedInfo.getAngularVelocity());
                            break;
                        case position:
                            updateCurrentPosition(interestedInfo.getPosition());
                            updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum.positionController, interestedInfo.getPosition());
                            updateMovingStatus(interestedInfo.isMoving());
                            break;
                        case move_status:
                            updateMovingStatus(interestedInfo.isMoving());
                            break;
                        case set_feedback_time:
                            updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum.feedbackTime, interestedInfo.getFeedbackTime_100ms());
                            DebugLog.e("~~~~~~set_feedback_time = " + interestedInfo.getFeedbackTime_100ms());
                            break;
                    }

                }
            });
        }
    }

    private void updateSeekBarWithSeekBarControlEnum(SeekBarControlEnum controlEnum, int value) {
        if (controlEnum == null) {
            DebugLog.e(" controlEnum is null");
            return;
        }

        LayoutSeekBarViewHolder holder = seekBarControlEnumMap.get(controlEnum);
        if (holder == null) {
            DebugLog.e(" holder is null for controlEnum = " + controlEnum);
            return;
        }

        if (holder.getSeekBar() != null) {
            holder.getSeekBar().setProgress(value);
        }

        switch (controlEnum) {
            case minAngle:
            case maxAngle:
                updatePositionSeekBarMinOrMax(controlEnum == SeekBarControlEnum.minAngle, value);
                break;
        }
    }

    private void updatePositionSeekBarMinOrMax(boolean min, int value) {
        LayoutSeekBarViewHolder holder = seekBarControlEnumMap.get(SeekBarControlEnum.positionController);
        if (holder == null) {
            DebugLog.e(" holder is for positionController");
            return;
        }

        BubbleSeekBar seekBar = holder.getSeekBar();

        if (seekBar == null) {
            DebugLog.e(" holder seekbar is null");
            return;
        }

        BubbleConfigBuilder builder = seekBar.getConfigBuilder();
        if (builder == null) {
            return;
        }
        if (min) {
            builder.min(value);
        } else {
            builder.max(value);
        }

        if (builder.getSectionTextColor() == Color.RED) {
            builder.sectionTextColor(Color.BLUE);
        } else {
            builder.sectionTextColor(Color.RED);
        }
        builder.build();

    }


    private void updateMovingStatus(boolean move) {
        String status = getString(R.string.no);
        if (move) {
            status = getString(R.string.yes);
        }
        txtMovingStatus.setText(getString(R.string.status_moving, status));
    }

    private void updateCurrentPosition(int position) {
        txtCurrentPosition.setText(getString(R.string.status_current_position, position));
    }

    private void updateCurrentAngularVelocity(int velocity) {
        txtCurrentAngularVelocity.setText(getString(R.string.status_current_angular_velocity, velocity));
    }

    private void actionImgSubOrAdd(View view, boolean add) {

        if (!(view.getTag() instanceof SeekBarControlEnum)) {
            DebugLog.e("view.getTag() is not instanceof SeekBarControlEnum, view.getTag() = " + view.getTag());
        }

        SeekBarControlEnum seekBarControlEnum = (SeekBarControlEnum) view.getTag();

        LayoutSeekBarViewHolder holder = seekBarControlEnumMap.get(seekBarControlEnum);
        if (holder == null) {
            DebugLog.e(seekBarControlEnum + ", holder is null");
            return;
        }

        BubbleSeekBar seekBar = holder.getSeekBar();
        if (seekBar == null) {
            DebugLog.e(seekBarControlEnum + ", seekBar is null");
            return;
        }

        int progress = seekBar.getProgress();

        if (!add) {
            progress--;

        } else {
            progress++;
        }

        if (progress < 0) {
            DebugLog.e(seekBarControlEnum + ", invalid seekBar progress(" + progress + ")");
            return;
        }

        if (progress > seekBar.getMax()) {
            DebugLog.e(seekBarControlEnum + ", invalid seekBar progress(" + progress + "), max is " + seekBar.getMax());
            return;
        }

        seekBar.setProgress(progress);

        sendServoValue(seekBarControlEnum, progress);

    }


    private void actionSeekBar(BubbleSeekBar seekBar, int progress) {

        if (!(seekBar.getTag() instanceof SeekBarControlEnum)) {
            DebugLog.e("seekBar.getTag() is not instanceof SeekBarControlEnum, seekBar.getTag() = " + seekBar.getTag());
        }

        SeekBarControlEnum seekBarControlEnum = (SeekBarControlEnum) seekBar.getTag();

        sendServoValue(seekBarControlEnum, progress);

    }

    private void sendServoValue(SeekBarControlEnum seekBarControlEnum, int value) {

        IServoControl servoControl = getServoControl();
        switch (seekBarControlEnum) {
            case minAngle:
                servoControl.setAngleAreaMin(value);
                break;
            case maxAngle:
                servoControl.setAngleAreaMax(value);
                break;
            case positionController:
                servoControl.setPosition(value);
                break;
            case angularVelocity:
                servoControl.setAngularVelocity(value);
                break;
            case feedbackTime:
                servoControl.setFeedbackTime_100ms(value);
                break;

        }

    }

    private final class LayoutSeekBarViewHolder {
        private final View sub;
        private final View add;
        private final BubbleSeekBar seekBar;

        public LayoutSeekBarViewHolder(@NonNull View container) {

            sub = container.findViewById(R.id.sub);
            add = container.findViewById(R.id.add);
            seekBar = (BubbleSeekBar) container.findViewById(R.id.seekBar);

            if (sub != null) {
                sub.setTag(container.getTag());
                sub.setOnClickListener(FragmentServo.this);
            }
            if (add != null) {
                add.setTag(container.getTag());
                add.setOnClickListener(FragmentServo.this);
            }
            if (seekBar != null) {
                seekBar.setTag(container.getTag());
                seekBar.setOnProgressChangedListener(FragmentServo.this);
                if (container.getTag() instanceof SeekBarControlEnum) {
                    int minRes = ((SeekBarControlEnum) container.getTag()).getMinValueRes();
                    int maxRes = ((SeekBarControlEnum) container.getTag()).getMaxValueRes();

                    int min = container.getContext().getResources().getInteger(minRes);
                    int max = container.getContext().getResources().getInteger(maxRes);

                    seekBar.getConfigBuilder()
                            .min(min)
                            .max(max)
                            .progress(min).build();
                }
            }
        }

        public View getSub() {
            return sub;
        }

        public View getAdd() {
            return add;
        }

        public BubbleSeekBar getSeekBar() {
            return seekBar;
        }
    }


    private enum SeekBarControlEnum {

        minAngle(R.id.layoutMinAngle, R.integer.angle_min, R.integer.angle_max),
        maxAngle(R.id.layoutMaxAngle,  R.integer.angle_min, R.integer.angle_max),
        positionController(R.id.layoutPositionController,  R.integer.angle_min, R.integer.angle_max),
        angularVelocity(R.id.layoutAngularVelocity,  R.integer.angular_velocity_min, R.integer.angular_velocity_max),
        feedbackTime(R.id.layoutTurn, R.integer.feedback_time_min, R.integer.feedback_time_max),

        ;
        final int containerViewRes;
        final int minValueRes;
        final int maxValueRes;

        private SeekBarControlEnum(int containerViewRes, int min, int max) {
            this.containerViewRes = containerViewRes;
            this.minValueRes = min;
            this.maxValueRes = max;
        }

        public int getContainerViewRes() {
            return containerViewRes;
        }

        public int getMinValueRes() {
            return minValueRes;
        }

        public int getMaxValueRes() {
            return maxValueRes;
        }

    }


    private IServoControl getServoControl() {
        if (switchEnabled.isChecked()) {
            return ServoSingleton.INSTANCE.getServoControl();
        }

        return dummyServoControl;
    }

    private final DummyServoControl dummyServoControl = new DummyServoControl(null, null);

    private final class DummyServoControl extends IServoControl {
        public DummyServoControl(IServoCommand command, IServoReadDataAnalyze analyze) {
            super(command, analyze);
        }

        @Override
        public void servoStop() {

        }

        @Override
        public void turn(boolean b, int i) {

        }

        @Override
        protected void servoControl(ServoControlAttr servoControlAttr, ServoControlAttr.Instruction instruction, int i) {

        }

        @Override
        protected void servoInit() {

        }

        @Override
        public void onWriteData(UUID uuid, ByteBuffer byteBuffer) {

        }

        @Override
        public void onReadData(UUID uuid, byte[] bytes, int i) {

        }
    }

}
